package zdream.pmfield.test.stage.effect;

import org.junit.Before;
import org.junit.Test;

import zdream.pmfield.core.Result;
import zdream.pmfield.data.foe.Foe;
import zdream.pmfield.data.foe.MoveTemplate;
import zdream.pmfield.stage.StageBuilderForTest;
import zdream.pmfield.stage.StageWrapper;
import zdream.pmfield.stage.action.AttackReport;
import zdream.pmfield.stage.message.ActionRequestMessage;
import zdream.pmfield.stage.message.AttackMessage;
import zdream.pmfield.stage.message.FoeAddBuffMessage;
import zdream.pmfield.stage.message.FoeRemoveBuffMessage;
import zdream.pmfield.stage.message.RollReportMessage;
import zdream.pmfield.stage.message.TurnStartMessage;
import zdream.pmfield.storage.FoeTemplateStorage;
import zdream.pmfield.test.utils.ResourceConstant;
import zdream.pmfield.test.utils.StageConstant;

import static org.junit.Assert.*;

/**
 * @author Zdream
 * @date 2023-12-08
 * @since 0.0.1
 */
public class FlyingBuffCase {
	FoeTemplateStorage storage;

	@Before
	public void before() {
		storage = new FoeTemplateStorage();
		storage.load();
		storage.loadMovesInResource(ResourceConstant.RES_PATH_TEST_MOVES_JSON);
	}

	@Test
	public void testFlyingBuff() {
		MoveTemplate flyingMove = storage.findMoveById(10003, 1);
		assertEquals("起飞", flyingMove.name);
		MoveTemplate normalMove = storage.findMoveById(10000015, 1);
		assertEquals("无效果", normalMove.name);
		MoveTemplate electricMove = storage.findMoveById(10000108, 1);
		assertEquals("电", electricMove.name);
		MoveTemplate iceMove = storage.findMoveById(10000109, 1);
		assertEquals("冰", iceMove.name);

		Foe foe1 = Foe.builder(storage.findFoeById(142))
				.withLevel(30)
				.withMove(normalMove)
				.withMove(flyingMove)
				.skipShuffleVStat()
				.build();
		Foe foe2 = Foe.builder(storage.findFoeById(137))
				.withLevel(30)
				.withMove(normalMove)
				.withMove(electricMove)
				.withMove(iceMove)
				.skipShuffleVStat()
				.build();

		final StageWrapper wrapper = StageBuilderForTest.builder()
				.putFoeToOneSide(foe1)
				.putFoeToAnotherSide(foe2)
				.withStorage(storage)
				.disableDistanceCheck()
				.buildWrapper();

		System.out.println("TEST 1: 一开始砂免疫电");

		wrapper.printBeforeHandleMessage();
		wrapper.onDefaultActionRequest(foe1);
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe2);

		wrapper.runUntil(ActionRequestMessage.class, message -> {
			assertEquals(foe2, message.foe.foe);
			final Result commit = message.createReplyTemplate()
					.withMove(electricMove)
					.withTarget(foe1)
					.commit();
			assertTrue(commit.errorMessage, commit.success);
			return true;
		});

		wrapper.runUntil(AttackMessage.class, message -> {
			assertTrue(message.report.action.isForAttack());
			assertEquals(foe2, message.report.action.asAttack().foe.foe);
			assertEquals(1, message.report.items.size());

			final AttackReport.AttackReportItem item = message.report.items.get(0);
			assertEquals(foe1, item.foe.foe);
			assertEquals(0, 0, item.typeFixed);
			return true;
		});

		System.out.println("TEST 2: 试试看冰的效果怎么样");
		wrapper.clearOnMessageHooks();
		wrapper.onDefaultActionRequest(foe1);
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe2);

		wrapper.runUntil(ActionRequestMessage.class, message -> {
			assertEquals(foe2, message.foe.foe);
			final Result commit = message.createReplyTemplate()
					.withMove(iceMove)
					.withTarget(foe1)
					.commit();
			assertTrue(commit.errorMessage, commit.success);
			return true;
		});

		float[] iceRates = new float[1];

		wrapper.runUntil(AttackMessage.class, message -> {
			assertTrue(message.report.action.isForAttack());
			assertEquals(foe2, message.report.action.asAttack().foe.foe);
			assertEquals(1, message.report.items.size());

			final AttackReport.AttackReportItem item = message.report.items.get(0);
			assertEquals(foe1, item.foe.foe);
			iceRates[0] = item.typeFixed;
			return true;
		});

		System.out.println("TEST 3: 飞起来");

		wrapper.clearOnMessageHooks();
		wrapper.onDefaultActionRequest();
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe1);

		wrapper.clearOnMessageHooks();
		wrapper.runUntil(ActionRequestMessage.class, message -> {
			assertEquals(foe1, message.foe.foe);
			final Result commit = message.createReplyTemplate()
					.withMove(flyingMove)
					.commit();
			assertTrue(commit.errorMessage, commit.success);
			return true;
		});

		wrapper.runUntil(FoeAddBuffMessage.class, message -> {
			assertEquals(foe1, message.foe.foe);
			assertTrue(message.buff.is(StageConstant.BUFF_FLYING));
			assertTrue(message.success);
			return true;
		});

		System.out.println("TEST 4: 飞起来的时候, 砂不再免疫电");
		wrapper.clearOnMessageHooks();
		wrapper.onDefaultActionRequest(foe1);
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe2);

		wrapper.runUntil(ActionRequestMessage.class, message -> {
			assertEquals(foe2, message.foe.foe);
			final Result commit = message.createReplyTemplate()
					.withMove(electricMove)
					.withTarget(foe1)
					.commit();
			assertTrue(commit.errorMessage, commit.success);
			return true;
		});

		wrapper.runUntil(AttackMessage.class, message -> {
			assertTrue(message.report.action.isForAttack());
			assertEquals(foe2, message.report.action.asAttack().foe.foe);
			assertEquals(1, message.report.items.size());

			final AttackReport.AttackReportItem item = message.report.items.get(0);
			assertEquals(foe1, item.foe.foe);
			assertTrue(item.typeFixed > 1);
			return true;
		});

		System.out.println("TEST 5: 飞起来的时候, 砂不再免疫电");
		wrapper.clearOnMessageHooks();
		wrapper.onDefaultActionRequest(foe1);
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe2);

		wrapper.runUntil(ActionRequestMessage.class, message -> {
			assertEquals(foe2, message.foe.foe);
			final Result commit = message.createReplyTemplate()
					.withMove(iceMove)
					.withTarget(foe1)
					.commit();
			assertTrue(commit.errorMessage, commit.success);
			return true;
		});

		wrapper.runUntil(AttackMessage.class, message -> {
			assertTrue(message.report.action.isForAttack());
			assertEquals(foe2, message.report.action.asAttack().foe.foe);
			assertEquals(1, message.report.items.size());

			final AttackReport.AttackReportItem item = message.report.items.get(0);
			assertEquals(foe1, item.foe.foe);
			assertTrue(item.typeFixed > iceRates[0]);
			return true;
		});
	}

	@Test
	public void testGroundMoveAttackFlyingFoe() {
		MoveTemplate flyingMove = storage.findMoveById(10003, 1);
		assertEquals("起飞", flyingMove.name);
		MoveTemplate normalMove = storage.findMoveById(10000015, 1);
		assertEquals("无效果", normalMove.name);
		MoveTemplate groundMove = storage.findMoveById(341, 1);
		assertEquals("泥巴射击", groundMove.name);

		Foe foe1 = Foe.builder(storage.findFoeById(142))
				.withLevel(30)
				.withMove(normalMove)
				.withMove(flyingMove)
				.skipShuffleVStat()
				.build();
		Foe foe2 = Foe.builder(storage.findFoeById(137))
				.withLevel(30)
				.withMove(groundMove)
				.skipShuffleVStat()
				.build();

		final StageWrapper wrapper = StageBuilderForTest.builder()
				.putFoeToOneSide(foe1)
				.putFoeToAnotherSide(foe2)
				.withStorage(storage)
				.disableDistanceCheck()
				.buildWrapper();

		System.out.println("TEST 1: 地面能击中非飞行的 foe");

		wrapper.printBeforeHandleMessage();
		wrapper.onDefaultActionRequest();
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe2);

		wrapper.runUntil(AttackMessage.class, message -> {
			assertTrue(message.report.action.isForAttack());
			assertEquals(foe2, message.report.action.asAttack().foe.foe);
			assertEquals(groundMove, message.report.action.asAttack().move.move);
			assertEquals(1, message.report.items.size());

			final AttackReport.AttackReportItem item = message.report.items.get(0);
			assertEquals(foe1, item.foe.foe);
			assertTrue(item.typeFixed > 0);
			return true;
		});

		System.out.println("TEST 2: 飞起来");

		wrapper.clearOnMessageHooks();
		wrapper.onDefaultActionRequest();
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe1);

		wrapper.clearOnMessageHooks();
		wrapper.runUntil(ActionRequestMessage.class, message -> {
			assertEquals(foe1, message.foe.foe);
			final Result commit = message.createReplyTemplate()
					.withMove(flyingMove)
					.commit();
			assertTrue(commit.errorMessage, commit.success);
			return true;
		});

		wrapper.runUntil(FoeAddBuffMessage.class, message -> {
			assertEquals(foe1, message.foe.foe);
			assertTrue(message.buff.is(StageConstant.BUFF_FLYING));
			assertTrue(message.success);
			return true;
		});

		System.out.println("TEST 3: 飞起来之后有三个行动骰子");

		wrapper.clearOnMessageHooks();
		wrapper.onDefaultActionRequest();
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe1);

		wrapper.runUntil(RollReportMessage.class, message -> {
			if (!StageConstant.ROLL_TITLE_MOVE_POINT.equals(message.title)) {
				return false;
			}

			assertEquals(foe1, message.foe.foe);
			assertEquals(3, message.rolls.length);
			return true;
		});

		System.out.println("TEST 4: 飞起来之后地面技能打不了了");

		wrapper.clearOnMessageHooks();
		wrapper.onDefaultActionRequest();
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe2);

		wrapper.runUntil(AttackMessage.class, message -> {
			assertTrue(message.report.action.isForAttack());
			assertEquals(foe2, message.report.action.asAttack().foe.foe);
			assertEquals(1, message.report.items.size());

			final AttackReport.AttackReportItem item = message.report.items.get(0);
			assertEquals(foe1, item.foe.foe);
			assertEquals(0, 0, item.typeFixed);
			return true;
		});
	}

	@Test
	public void testRemoveFlyingEffect() {
		MoveTemplate flyingMove = storage.findMoveById(10003, 1);
		assertEquals("起飞", flyingMove.name);
		MoveTemplate landingMove = storage.findMoveById(10004, 1);
		assertEquals("落地", landingMove.name);
		MoveTemplate normalMove = storage.findMoveById(10000015, 1);
		assertEquals("无效果", normalMove.name);

		Foe foe1 = Foe.builder(storage.findFoeById(142))
				.withLevel(30)
				.withMove(normalMove)
				.withMove(flyingMove)
				.withMove(landingMove)
				.skipShuffleVStat()
				.build();
		Foe foe2 = Foe.builder(storage.findFoeById(137))
				.withLevel(30)
				.withMove(normalMove)
				.skipShuffleVStat()
				.build();

		final StageWrapper wrapper = StageBuilderForTest.builder()
				.putFoeToOneSide(foe1)
				.putFoeToAnotherSide(foe2)
				.withStorage(storage)
				.disableDistanceCheck()
				.buildWrapper();

		System.out.println("TEST 1: 飞起来");

		wrapper.printBeforeHandleMessage();
		wrapper.clearOnMessageHooks();
		wrapper.onDefaultActionRequest();
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe1);

		wrapper.clearOnMessageHooks();
		wrapper.runUntil(ActionRequestMessage.class, message -> {
			assertEquals(foe1, message.foe.foe);
			final Result commit = message.createReplyTemplate()
					.withMove(flyingMove)
					.commit();
			assertTrue(commit.errorMessage, commit.success);
			return true;
		});

		wrapper.runUntil(FoeAddBuffMessage.class, message -> {
			assertEquals(foe1, message.foe.foe);
			assertTrue(message.buff.is(StageConstant.BUFF_FLYING));
			assertTrue(message.success);
			return true;
		});

		System.out.println("TEST 2: 再落地");

		wrapper.clearOnMessageHooks();
		wrapper.onDefaultActionRequest();
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe1);

		wrapper.clearOnMessageHooks();
		wrapper.runUntil(ActionRequestMessage.class, message -> {
			assertEquals(foe1, message.foe.foe);
			final Result commit = message.createReplyTemplate()
					.withMove(landingMove)
					.commit();
			assertTrue(commit.errorMessage, commit.success);
			return true;
		});

		wrapper.runUntil(FoeRemoveBuffMessage.class, message -> {
			assertEquals(foe1, message.foe.foe);
			assertTrue(message.buff.is(StageConstant.BUFF_FLYING));
			return true;
		});
	}
}
